#include "gtest/gtest.h"


#include "base.h"
#include "IRocksdb.h"
#include "Cast.h"
#include "CastComparator.h"
#include "OptionsArgs.h"

//#include "rocksdb/comparator.h"

namespace helper {
namespace demo {

template<typename T>
class CastComparableRocksdb : public IRocksdb<Slice, Slice>{
public:
  using cast_type = T;
  using cast_visitor = std::function<bool (const cast_type &, const value_type &)>;

  using super = IRocksdb<Slice, Slice>;
  using key_type = typename super::key_type;
  using value_type = typename super::value_type;
  using visitor = typename super::visitor; 
  using iterator = typename super::iterator;

  CastComparableRocksdb(super & db, const Cast<cast_type> & cast)
  : db_(db), cast_(cast), comparator_(*newCastComparator<cast_type>(cast))
  {
  }
  ~CastComparableRocksdb() override{
    this->close();
  }

  const std::string & name() const override{
    return db_.name();
  }

  const OptionsArgs & optionsArgs() const override{
    return db_.optionsArgs();
  }
  OptionsArgs & optionsArgs()  override {
    return db_.optionsArgs();
  }

  IDBInstance::DBImpl * instance() override {
    return db_.instance();
  }

  Status put(const key_type & key, const value_type & value) override {
    return db_.put(key, value);
  }
  Status get(const key_type & key, std::string & value) const override{
    return db_.get(key, value);
  }
  Status del(const key_type & key) override {
    return db_.del(key);
  }
  bool foreach(const visitor & v, const key_type & start, const key_type & end) const override {
    return this->foreach(optionsArgs().read(), v, start, end);
  }
  iterator * range(const key_type& start, const key_type& end) const override {
    return this->range(optionsArgs().read(), start, end);
  }


  Status put(const WriteOptions & write, const key_type & key, const value_type & value) override {
    return db_.put(write, key, value);
  }
  Status get(const ReadOptions & read, const key_type & key, std::string & value) const override {
    return db_.get(read, key, value);
  }
  Status del(const WriteOptions & write, const key_type & key) override {
    return db_.del(write, key);
  }
  bool foreach(const ReadOptions& read, const visitor & v, const key_type& start, const key_type & end) const override {
    return db_.foreach(read, v, start, end);
  }
  iterator * range(const ReadOptions & read, const key_type& start, const key_type& end) const override {
    return db_.range(read, start, end);
  }


  // new cast functions 
  Status put(const cast_type & key, const value_type & value)  {
    return db_.put(cast_.toSlice(key), value);
  }
  Status get(const cast_type & key, std::string & value) const {
    return db_.get(cast_.toSlice(key), value);
  }
  Status del(const cast_type & key)  {
    return db_.del(cast_.toSlice(key));
  }
  bool foreach(const cast_visitor & cv, const cast_type & start, const cast_type & end) const  {
    return this->foreach(this->optionsArgs().read(), cv, start, end);
  }
  iterator * range(const cast_type& start, const cast_type& end) const  {
    return this->range(this->optionsArgs().readd(), start, end);
  }


  Status put(const WriteOptions & write, const cast_type & key, const value_type & value)  {
    return db_.put(write, cast_.toSlice(key), value);
  }
  Status get(const ReadOptions & read, const cast_type & key, std::string & value) const  {
    return db_.get(read, cast_.toSlice(key), value);
  }
  Status del(const WriteOptions & write, const cast_type & key)  {
    return db_.del(write, cast_.toSlice(key));
  }
  bool foreach(const ReadOptions& read, const cast_visitor & cv, const cast_type& start, const cast_type & end) const  {
    auto v = [&cv, this](const Slice & k , const Slice & v)->bool{
      auto && [ok, sliceK] = this->cast_.fromSlice(k);
      if(ok){
        return cv(sliceK, v);
      }
      else 
        return false;
    };
    return db_.foreach(read, v, cast_.toSlice(start), cast_.toSlice(end));
  }

  iterator * range(const ReadOptions & read, const cast_type& start, const cast_type& end) const  {
    //TODO: new iterator ????
    return db_.range(read, cast_.toSlice(start), cast_.toSlice(end));
  }
protected:
	bool _open() override {
    //db_.optionsArgs().open().comparator = &comparator_;
    return db_.open();
  }
	bool _close() override {
    return db_.close();
  }

private:
  super & db_;
  Cast<cast_type> cast_;
  const ::rocksdb::Comparator & comparator_;
};

}//namespace demo
}//namespace helper

#include "Rocksdb.h"

using namespace helper;
using namespace demo;

TEST(CastComparableRocksdbTest, testMain)
{ 
  OptionsArgs args;
  args.open().create_if_missing = true;
  auto * rdb = new Rocksdb("cast-hello.db", args);
  {
    auto * cdb = new CastComparableRocksdb<uint32_t>(*rdb, Cast<uint32_t>("uint32_t.cast.comparator"));
    ASSERT_TRUE(cdb->open());
    const uint32_t k = 12;
    cdb->put(k, "value");

    std::string value;
    cdb->get(k, value);
    ASSERT_EQ(value, std::string("value"));

    auto print = [](const uint32_t & k , const Slice & v)->bool{
      std::cout << "key:" << k  << ", value:" << v.ToString() << std::endl;
      return true;
    };
    cdb->foreach(print, 0u, 100000u);
    cdb->del(12u);

    delete cdb;
  }
  delete rdb;
}

TEST(CastComparableRocksdb, testRange){
  //TODO: New CastComparatorRockdsdb::Iterator(RocksdbIterator)
}
